package baiqitun.stupro.security.service.impl;

import baiqitun.stupro.common.exception.enums.support.StuproExceptionEnum;
import baiqitun.stupro.common.exception.enums.support.ExistedExceptionEnum;
import baiqitun.stupro.common.exception.enums.support.NotFoundExceptionEnum;
import baiqitun.stupro.common.exception.support.NotFoundException;
import baiqitun.stupro.common.exception.support.RelationExistedException;
import baiqitun.stupro.common.exception.StuproException;
import baiqitun.stupro.common.service.support.MyServiceImpl;
import baiqitun.stupro.security.enums.SysRoleEnum;
import baiqitun.stupro.security.entity.SysRole;
import baiqitun.stupro.security.entity.SysUser;
import baiqitun.stupro.security.entity.SysUserRole;
import baiqitun.stupro.security.mapper.SysUserRoleMapper;
import baiqitun.stupro.security.service.SysRoleService;
import baiqitun.stupro.security.service.SysUserRoleService;
import baiqitun.stupro.security.service.SysUserService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotBlank;
import java.util.LinkedList;
import java.util.List;

/**
 * <p>
 * 用户角色表 服务实现类
 * </p>
 *
 * @author kedaji
 * @since 2020-03-25
 */
@Service
public class SysUserRoleServiceImpl extends MyServiceImpl<SysUserRoleMapper, SysUserRole> implements SysUserRoleService {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;

    /**
     * 管理员用户拥有以下角色权限
     * ROLE_ANONYMOUS(匿名)
     * ROLE_ADMINISTRATOR(管理员)
     * @param user 用户信息
     */
    @CachePut("userRole")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveAdministrator(@Validated SysUser user) {
        sysUserService.save(user);
        grantRoleForUser(user.getId(), SysRoleEnum.ROLE_ADMINISTRATOR.name(),  user.getCreateBy());
        grantRoleForUser(user.getId(), SysRoleEnum.ROLE_STUDENT.name(), user.getCreateBy());
        grantRoleForUser(user.getId(), SysRoleEnum.ROLE_TUTOR.name(), user.getCreateBy());
        grantRoleForUser(user.getId(), SysRoleEnum.ROLE_USER.name(), user.getCreateBy());
    }


    @CachePut("userRole")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void grantRoleForUser(@NotBlank(message = "用户id不能为空") String userId,
                                 @NotBlank(message = "角色代码不能为空") String roleCode, String createBy) {
        SysRole role = sysRoleService.getRoleByCode(roleCode);
        if (role == null){
            throw new NotFoundException(NotFoundExceptionEnum.ROLE_NOT_FOUND).put(SysRole.class,"code", roleCode);
        }
        if (SysRoleEnum.ROLE_ANONYMOUS.name().equals(roleCode)){
            // 所有用户都有匿名用户的权限，此权限不需要手动赋予用户
            throw new StuproException(StuproExceptionEnum.ILLEGAL_OPERATION);
        }
        checkExistedByRelation(userId, role.getId());
        SysUserRole relation = new SysUserRole();
        relation.setUserId(userId).setRoleId(role.getId()).setCreateBy(createBy);
        save(relation);
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<SysRole> listRoleByUserId(String userId) {
        SysUserRole queryUserRole = new SysUserRole();
        queryUserRole.setUserId(userId);
        List<SysUserRole> relations = list(new QueryWrapper<>(queryUserRole));
        List<SysRole> roles = new LinkedList<>();
        for (SysUserRole tmpUserRole: relations){
            SysRole role = sysRoleService.getById(tmpUserRole.getRoleId());
            roles.add(role);
        }
        return roles;
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<SysUser> listAdministrator() {
        SysRole adminRole = sysRoleService.getRoleByCode(SysRoleEnum.ROLE_ADMINISTRATOR.name());
        if (adminRole == null){
            throw new NotFoundException(NotFoundExceptionEnum.ROLE_NOT_FOUND);
        }
        return listUserByRoleId(adminRole.getId());
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public List<SysUser> listUserByRoleId(String roleId) {
        checkNotFound(roleId, new NotFoundException(NotFoundExceptionEnum.ROLE_NOT_FOUND));
        SysUserRole queryUserRole = new SysUserRole();
        queryUserRole.setRoleId(roleId);
        List<SysUserRole> relations = list(new QueryWrapper<>());
        List<SysUser> users = new LinkedList<>();
        for (SysUserRole relation: relations){
            SysUser user = sysUserService.getById(relation.getUserId());
            users.add(user);
        }
        return users;
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public void checkUserHasRole(String userId, SysRoleEnum roleEnum) {
        if (! hasRole(userId, roleEnum)){
            throw new StuproException(StuproExceptionEnum.ILLEGAL_OPERATION);
        }
    }

    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public boolean hasRole(String userId, SysRoleEnum roleEnum) {
        return hasRelation(userId, sysRoleService.getRoleByCode(roleEnum.name()).getId());
    }


    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public boolean hasRelation(String userId, String roleId){
        SysUserRole queryUserRole = new SysUserRole();
        queryUserRole.setUserId(userId).setRoleId(roleId);
        return count(new QueryWrapper<>(queryUserRole)) > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean save(@Validated SysUserRole sysUserRole){
        checkExistedByRelation(sysUserRole.getUserId(), sysUserRole.getRoleId());
        return super.save(sysUserRole);
    }


    /**
     * 检查是否存在
     * @param userId 用户id
     * @param roleId 角色id
     */
    private void checkExistedByRelation(String userId, String roleId){
        if (hasRelation(userId, roleId)){
            throw new RelationExistedException(ExistedExceptionEnum.RELATION_USER_ROLE_EXISTED).put(SysUser.class,"id", userId)
                    .put(SysRole.class, "id", roleId);
        }
    }
}
